<template>
  <el-dialog :title="title" :visible="assignShowDialog" width="30%" :before-close="handleClose" @open="openDialog">
    <!-- 在初始化加载时，使用 :check-strictly="true" 来确保只有明确指定的节点会被选中。这解决了首次加载时不会自动选中父节点下所有子节点的问题 -->
    <!--@check-change="handleCheckChange"当用户手动点击某个父节点的复选框时，我们需要监听这种变化，并据此更新所有子节点的选择状态  -->
    <el-tree
      ref="tree"
      :data="treeData"
      show-checkbox
      :default-expand-all="false"
      :expand-on-click-node="false"
      node-key="menuId"
      highlight-current
      :props="defaultProps"
      :default-checked-keys="defaultChecked"
      :check-strictly="true"
      @check-change="handleCheckChange"
    />
    <span slot="footer" class="dialog-footer">
      <el-button @click="handleClose">取 消</el-button>
      <el-button type="primary" @click="doAssign">确 定</el-button>
    </span>
  </el-dialog>
</template>

<script>
  import { toAssign, doAssign } from '@/api/role'
  export default {
    props: {
      title: {
        type: String,
        default: ''
      },
      assignShowDialog: {
        type: Boolean,
        default: false
      },
      roleId: {
        type: String,
        default: ''
      }
    },
    data() {
      return {
        treeData: [], // 树形数据
        defaultChecked: [], // 选中状态的数据
        defaultProps: {
          children: 'children',
          label: 'menuName'
        }
      }
    },
    methods: {
      async loadData() {
        const res = await toAssign(this.roleId)
        // 将后端返回的树形结构数据，交给我们的tree树形组件
        this.treeData = res
        // 过滤收集到全部的 selected=true的数据的menuId
        this.defaultChecked = this.getSelectedIds(res)
      },
      openDialog() {
        this.loadData()
      },
      /**
       * 获取所有 selected 为 true 的节点的 id
       * @param nodes 树形数据数组
       * @returns {Array} 包含所有选中节点 id 的数组
       */
      getSelectedIds(nodes) {
        let ids = []
        nodes.forEach(node => {
          if (node.selected) {
            ids.push(node.menuId)
          }
          if (node.children && node.children.length > 0) {
            ids = ids.concat(this.getSelectedIds(node.children))
          }
        })
        return ids
      },

      handleClose() {
        this.$emit('update:assignShowDialog', false)
        // 清除所有选中的节点
        if (this.$refs.tree) {
          this.$refs.tree.setCheckedKeys([])
          this.treeData = []
          this.defaultChecked = []
        }
      },
      /**
       * 分配角色
       */
      async doAssign() {
        const assignRoleMenuDto = {
          // 获取到所有被勾选的menuId
          menuIds: this.$refs.tree.getCheckedKeys(),
          roleId: this.roleId
        }
        await doAssign(assignRoleMenuDto)
        this.$message.success('分配角色成功')
        this.handleClose()
      },
      handleCheckChange(data, checked) {
        // 如果当前节点被选中
        if (checked) {
          this.checkAllChildren(data.menuId, true) // 递归选中所有子节点
        } else {
          this.uncheckAllChildren(data.menuId) // 取消选中所有子节点
          this.uncheckParent(data.menuId) // 检查并可能取消选中父节点
        }
      },

      checkAllChildren(menuId, checked) {
        const tree = this.$refs.tree
        const node = this.findNodeById(this.treeData, menuId)

        if (!node) return
        tree.setChecked(node.menuId, checked) // 设置当前节点状态
        if (node.children && node.children.length > 0) {
          node.children.forEach(child => {
            this.checkAllChildren(child.menuId, checked)
          })
        }
      },

      uncheckAllChildren(menuId) {
        const tree = this.$refs.tree
        const node = this.findNodeById(this.treeData, menuId)

        if (!node) return

        tree.setChecked(node.menuId, false) // 取消选中当前节点

        if (node.children && node.children.length > 0) {
          node.children.forEach(child => {
            this.uncheckAllChildren(child.menuId)
          })
        }
      },

      uncheckParent(menuId) {
        const tree = this.$refs.tree
        const parentNode = this.getParentNode(menuId)

        if (!parentNode) return

        // 检查父节点的所有子节点是否都被取消选中
        const allChildrenUnchecked = parentNode.children.every(child => !tree.getCheckedKeys().includes(child.menuId))

        if (allChildrenUnchecked) {
          tree.setChecked(parentNode.menuId, false) // 取消选中父节点
          this.uncheckParent(parentNode.menuId) // 递归检查父节点的父节点
        }
      },

      findNodeById(nodes, menuId) {
        for (let i = 0; i < nodes.length; i++) {
          if (nodes[i].menuId === menuId) {
            return nodes[i]
          }
          if (nodes[i].children) {
            const foundNode = this.findNodeById(nodes[i].children, menuId)
            if (foundNode) return foundNode
          }
        }
        return null
      },

      getParentNode(menuId) {
        const findParent = (nodes, id) => {
          for (let i = 0; i < nodes.length; i++) {
            if (nodes[i].children) {
              if (nodes[i].children.some(child => child.menuId === id)) {
                return nodes[i]
              } else {
                const parent = findParent(nodes[i].children, id)
                if (parent) return parent
              }
            }
          }
          return null
        }

        return findParent(this.treeData, menuId)
      }
    }
  }
</script>

<style></style>
